Before we have a look at the deprecations, first I would like to present a few practical innovations. Of course, this list is not complete, and is just a brief overview. The complete list of PHP 8.2 features can be found at [1].
Constants in traits
Previously, constants were only allowed in an interface or a class. In the new version, we are now also allowed to define constants in a trait [2]. This means that it is possible to use a constant from a trait in an enum (Listing 1).
trait T { private const CONSTANT = 42; } enum E: int { use T; case CaseA = self::CONSTANT; }
However, accessing the constant has a few restrictions. First, you cannot define the same constant in an interface. Second, the visibility of the constant in a class cannot be overridden. You can only override the constant in the class where the trait is also used. If the constant was defined in a child class a trait is used in the parent class, this results in a fatal error. Furthermore, constants in a trait can be declared as final.
IPC NEWSLETTER
All news about PHP and web development
Null and false as return types
Ever since PHP 8.0, there are Union Types that we can use already to combine several Return Types. With Union Types, a special Return Type false was also defined, but you could not define it as nullable [2].
If a method returns null or false, we can only currently define the return type with boolean or null. However, boolean is not quite right here, because it can also output true. The new version also lets us return false and null as a return type [3].
True as its own type
While we’re defining false as its own type, we can also do the same with true in PHP 8.2 [4]. In PHP, there are some functions that internally return nothing but true. For example: SplFixedArray::setSize() and array_sort. However, these currently have boolean defined as the return type. For static analysis tools like PHPStan, this means you should still check the method for a false just to be on the safe side.
In particular, we’d like to include more precise definitions in our code with these return types, so static analysis tools don’t need unnecessary if statements.
The return type must not be combined with other boolean return types: true|false or bool|true leads to errors.
New PDO classes
PDO is a generic database class that supports multiple databases. Since some databases have their own special configuration capabilities, the PDO class has database-specific methods. For example, in PDO the sqliteCreateFunction method works in an SQLite context but not in MySQL.
Now, PHP 8.2 brings a factory method connect [5]. This method returns a special child class of PDO, containing database-specific methods. Therefore, new classes have been added: PDOSQLite, PDOPostgres, and PDOSqlite.
The PDO constructor stays so the old code still works, but it’s recommended you use $connection = PDO::connect($dsn,$usernamer,$password,$options);.
readonly classes
In PHP 8.1, we can define properties as readonly; then they can no longer be modified. Still, defining an immutable class isn’t so easy, especially if it has many properties. In PHP 8.2, you can put readonly in front of a class [6]. This will aso declare all properties in the class as readonly.
However, you must make sure that the public properties in the readonly class always have a type defined, and static properties are no longer permitted. Additionally, readonly classes may not derive normal classes or be derived from them. Inheritance only happens via readonly classes.
Hide sensitive parameters
Error messages are very important for developers. But again and again, certain error messages also display sensitive values, like PDO for example. Every now and then, an error message like the one shown in Listing 2 occurs.
PDOException: SQLSTATE[HY000] [2002] No such file or directory in /var/www/html/test.php:3 Stack trace: #0 /var/www/html/test.php(3): PDO->__construct('mysql:host=loca...', 'root', 'password') #1 {main}
Unfortunately, this kind of error message also displays a username and password, which of course, should not be seen by everyone. In PHP 8.2, there is now a new attribute #[\SensitiveParameter] [7]. If you define it before a parameter, then its content will not display in the error message (Listing 3). The value is also hidden in the exception stack trace.
function test( $foo, #[\SensitiveParameter] $bar, $baz ) { throw new \Exception('Error'); } test('foo', 'bar', 'baz'); /* Fatal error: Uncaught Exception: Error in test.php:8 Stack trace: #0 test.php(11): test('foo', Object(SensitiveParameterValue), 'baz') #1 {main} thrown in test.php on line 8 */
No longer included: utf8_encode and utf8_decode
Let’s move on to the first backwards compatibility break. In the new version, the utf8_encode and utf8_decode functions are missing [8]. They were often used as a quickfix in PHP code, but in the wrong context since the function names are misleading. utf8_encode and utf8_decode only convert ISO-8859-1 to UTF-8 and vice versa. Some characters from the Windows-1252 encoding are not converted correctly because the encoding is not used. These functions also don’t have error messages for missing characters, which aids in their misuse.
YOU LOVE PHP?
Explore the PHP Core Track
PHP already offers three possibilities for converting UTF-8 to other encodings. So utf8_encode and utf8_decode are actually obsolete (Listing 4). In existing code, you should replace utf8_encode and utf8_decode with one of these alternatives.
// ext/mbstring: $utf8 = mb_convert_encoding($latin1, 'UTF-8', 'ISO-8859-1'); $latin1 = mb_convert_encoding($utf8, 'ISO-8859-1', 'UTF-8'); // ext/intl: $utf8 = UConverter::transcode($latin1, 'UTF8', 'ISO-8859-1'); $latin1 = UConverter::transcode($utf8, 'ISO-8859-1', 'UTF8'); // ext/iconv: $utf8 = iconv('ISO-8859-1', 'UTF-8', $latin1); $latin1 = iconv('UTF-8', 'ISO-8859-1', $utf8);
No longer included: context-dependent callables
In PHP 8.2, you receive a warning as soon as you call call_user_func inside a class with the context to the class [9]. At the moment, you can still call a method dynamically with call_user_func. This includes the calls in Listing 5. All of the calls are context-sensitive except for the last two. This means you have to pay attention to some things internally. For example, if you use self, first you must clarify at runtime which class the callable is called from.
"self::method" "parent::method" "static::method" ["self", "method"] ["parent", "method"] ["static", "method"] ["Foo", "Bar::method"] [new Foo, "Bar::method"]
In the future, we only want to consider public methods in the callables. With this change, it will be possible to define a callable as a property type. Private or static methods must be called with $callable() instead of call_user_func().
Instead of simple text, from now on you must refer to the class constant class, so that the callable function already knows in the definition which class it will be. Therefore, the code must be changed with search and replace (Listing 6). Here, the context is outsourced to the definition of the function and not just determined at runtime during the call.
"self::method" -> self::class . "::method" "parent::method" -> parent::class . "::method" "static::method" -> static::class . "::method" ["self", "method"] -> [self::class, "method"] ["parent", "method"] -> [parent::class, "method"] ["static", "method"] -> [static::class, "method"]
Deprecated ${} string interpolation
PHP allows variables in the string to be used with apostrophes. In the future, this will cause a deprecated warning [10]. Therefore, the following calls should be avoided:
var_dump("${foo}");
// Deprecated: Using ${} in strings is deprecated
var_dump("${(foo)}");
// Deprecated: Using ${} (variable variables) in strings is deprecated
Currently, there are three variants to output a variable in string. The latter will be removed in PHP 9.0:
$foo = 'foo';
var_dump("$foo");
var_dump("{$foo}");
var_dump("${foo}");
You should look for ${ in your code and replace it with {$ so that it will work without any problems in the future.
IPC NEWSLETTER
All news about PHP and web development
Dynamic properties
In PHP, it is possible to define dynamic properties in a class. In the past, this was often used in libraries where code should be defined dynamically depending on its configuration. In PHP 8.2, this also receives a deprecated warning [11]. The problem with dynamic properties is that it’s far too easy to miss an error (Listing 7).
class User { public $name; } $user = new User; $user->nane = "foo";
In the example shown in Listing 7, a typo would define another property in the class and you wouldn’t even know it until runtime. But the possibility for dynamic properties should stay. For this, you must define the class with a new attribute #[AllowDynamicProperties]. If you define the attribute on top of the class, then you can define properties on a class at runtime. They will not receive a deprecated warning anymore. Dynamic properties are useful for special classes, but not for the majority of classes. So you should use the attribute and control where they’re allowed and where they aren’t.
Conclusion
PHP 8.2 says goodbye to a lot of legacy code. This means you may need to clean up your code a bit, but the language is becoming more consistent. With the Rector PHP tool, upgrading to a new version should hardly be a problem. You can already see that a lot of things will be completely omitted in PHP 9. This makes the language less error-prone from side effects.
Links & Literature
[1] https://wiki.php.net/rfc#php_82
[2] https://wiki.php.net/rfc/constants_in_traits
[3] https://wiki.php.net/rfc/union_types_v2
[4] https://wiki.php.net/rfc/null-false-standalone-types
[5] https://wiki.php.net/rfc/true-type
[6] https://wiki.php.net/rfc/pdo_driver_specific_subclasses
[7] https://wiki.php.net/rfc/readonly_classes
[8] https://wiki.php.net/rfc/redact_parameters_in_back_traces
[9] https://wiki.php.net/rfc/remove_utf8_decode_and_utf8_encode
[10] https://wiki.php.net/rfc/deprecate_partially_supported_callables
[11] https://wiki.php.net/rfc/deprecate_dollar_brace_string_interpolation